package com.gavin.plugin.database.help;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashSet;

import com.gavin.model.DataType;
import com.gavin.model.JObject;
import com.gavin.model.Model;
import com.gavin.security.AES;
import com.gavin.utils.DateUtils;
import com.gavin.utils.IOUtils;

public class RecordUtils {

	/**
	 * 读取当前的记录数据
	 * @param rs
	 * @param strsFieldName
	 * @param naFieldType
	 * @param nsPrecision
	 * @param nsScale
	 * @return
	 * @throws SQLException
	 * @throws IOException
	 */
	public static JObject readField(ResultSet rs, String[] strsFieldName, int[] naFieldType, int[] nsPrecision, int[] nsScale) throws SQLException, IOException {
		if (rs.next() == false) {
			return null;
		}

		Object objValue = rs.getObject(1);
		if (objValue == null) {
			return null;
		}

		int nFieldType = naFieldType[0];
		switch (nFieldType) {
		case Types.BIT: {
			byte byValue = rs.getByte(1);
			if (byValue == 0) {
				return new JObject(DataType._boolean, false);
			} else {
				return new JObject(DataType._boolean, true);
			}
		}
		case Types.TINYINT: {
			return new JObject(DataType._byte, rs.getByte(1));
		}
		case Types.SMALLINT: {
			return new JObject(DataType._short, rs.getShort(1));
		}
		case Types.INTEGER: {
			return new JObject(DataType._int, rs.getInt(1));
		}
		case Types.BIGINT: {
			return new JObject(DataType._long, rs.getLong(1));
		}
		case Types.REAL: {
			return new JObject(DataType._float, rs.getFloat(1));
		}
		case Types.DOUBLE:
		case Types.FLOAT: {
			return new JObject(DataType._double, rs.getDouble(1));
		}
		case Types.DECIMAL:
		case Types.NUMERIC: {
			if (nsScale[0] == 0) {// 整数
				long lValue = rs.getLong(1);
				return new JObject(DataType._long, lValue);
			} else {// 小数
				return new JObject(DataType._double, rs.getDouble(1));
			}
		}
		case Types.VARCHAR:
		case Types.NVARCHAR:
		case Types.LONGVARCHAR:
		case Types.CHAR:
		case Types.NCHAR: {
			String strValue = rs.getString(1);
			return new JObject(DataType._string, strValue);
		}
		case Types.CLOB:
		case Types.NCLOB: {
			String strValue = "";
			Clob clobValue = rs.getClob(1);
			char[] chsValue = new char[(int) clobValue.length()];
			clobValue.getCharacterStream().read(chsValue);
			strValue = new String(chsValue);
			return new JObject(DataType._string, strValue.trim());
		}
		case Types.DATE:
		case Types.TIME:
		case Types.TIMESTAMP: {
			try {
				String strValue = DateUtils.timestamp2String(rs.getTimestamp(1),"yyyy-MM-dd HH:mm:ss");
				return new JObject(DataType._datetime, strValue);
			} catch (Exception e) {
				return null;
			}
		}
		case Types.BINARY:
		case Types.LONGVARBINARY: {
			byte[] bysValue = null;
			InputStream stream = null;
			try {
				stream = rs.getBinaryStream(1);
				bysValue = new byte[stream.available()];
				stream.read(bysValue);
			} finally {
				IOUtils.closeStream(stream);
			}

			return new JObject(DataType._bytes, bysValue);
		}
		case Types.BLOB: {
			byte[] bysValue = null;
			Blob blobValue = rs.getBlob(1);
			bysValue = blobValue.getBytes(1, (int) blobValue.length());
			return new JObject(DataType._bytes, bysValue);
		}
		}

		String strFieldName = strsFieldName[0];
		throw new SQLException("Unsupported sql data type " + nFieldType + "on field " + strFieldName);
	}
	
	public static Model readRecord(ResultSet rs, String[] strsFieldName, int[] naFieldType, int[] nsPrecision, int[] nsScale, HashSet<String> hsEncrypeFieldNames,AES aes) throws SQLException, IOException {
		Model modelRecord = new Model();
		if (RecordUtils.readRecord(rs, strsFieldName, naFieldType, nsPrecision, nsScale, hsEncrypeFieldNames,aes, modelRecord) == 0) {
			return null;
		}
		return modelRecord;
	}
	
	/**
	 * 读取当前的记录数据
	 * 
	 * @param rs
	 */
	public static int readRecord(ResultSet rs, String[] strsFieldName, int[] naFieldType, int[] nsPrecision, int[] nsScale, HashSet<String> hsDecrypeFieldNames,AES aes, Model modelRecord) throws SQLException, IOException {
		if (rs.next() == false) {
			return 0;
		}

		for (int i = 0; i < strsFieldName.length; i++) {
			String strFieldName = strsFieldName[i];
			if (strFieldName.length() == 0) {
				throw new SQLException("Please set alias name for field in select statement: " + strFieldName);
			}

			try {
				if (rs.getObject(i + 1) == null) {
					continue;
				}
			} catch (Exception e) {// 已反序列化对象,getObject应该是绝对安全的,不应该有异常,但有的driver处理空时会抛出异常,此处做兼容性处理,不需要抛出异常
				continue;
			}

			int nFieldType = naFieldType[i];
			switch (nFieldType) {
			case Types.BIT: {
				byte byValue = rs.getByte(i + 1);
				if (byValue == 0) {
					modelRecord.set(strFieldName, false);
				} else {
					modelRecord.set(strFieldName, true);
				}
				break;
			}
			case Types.TINYINT: {
				modelRecord.set(strFieldName, rs.getByte(i + 1));
				break;
			}
			case Types.SMALLINT: {
				modelRecord.set(strFieldName, rs.getShort(i + 1));
				break;
			}
			case Types.INTEGER: {
				modelRecord.set(strFieldName, rs.getInt(i + 1));
				break;
			}
			case Types.BIGINT: {
				modelRecord.set(strFieldName, rs.getLong(i + 1));
				break;
			}
			case Types.REAL: {
				modelRecord.set(strFieldName, rs.getFloat(i + 1));
				break;
			}
			case Types.DOUBLE:
			case Types.FLOAT: {
				modelRecord.set(strFieldName, rs.getDouble(i + 1));
				break;
			}
			case Types.DECIMAL:
			case Types.NUMERIC: {
				if (nsScale[i] == 0) {// 整数
					long lValue = rs.getLong(i + 1);
					modelRecord.set(strFieldName, lValue);
				} else {// 小数
					modelRecord.set(strFieldName, rs.getDouble(i + 1));
				}
				break;
			}
			case Types.VARCHAR:
			case Types.NVARCHAR:
			case Types.LONGVARCHAR:
			case Types.CHAR:
			case Types.NCHAR: {
				String strValue = rs.getString(i + 1);

				if (hsDecrypeFieldNames.contains(strFieldName.toLowerCase()) == true) {// 需要解密
					try {
						strValue = aes.decode(strValue);
					} catch (Exception e) {
					}
				}
				modelRecord.set(strFieldName, strValue);
				break;
			}
			case Types.CLOB:
			case Types.NCLOB: {
				String strValue = "";
				Clob clobValue = rs.getClob(i + 1);
				char[] chsValue = new char[(int) clobValue.length()];
				clobValue.getCharacterStream().read(chsValue);
				strValue = new String(chsValue);
				// strValue=Utility.encodingText(strValue,strCharset,strSystemCharset);
				modelRecord.set(strFieldName, strValue.trim());
				break;
			}
			case Types.DATE: {
				try {
					String strValue = DateUtils.timestamp2String(rs.getTimestamp(i + 1), "yyyy-MM-dd");
					modelRecord.set(strFieldName, strValue);
				} catch (Exception e) {
				}

				break;
			}
			case Types.TIME:
			case Types.TIMESTAMP: {
				try {
					String strValue = DateUtils.timestamp2String(rs.getTimestamp(i + 1), "yyyy-MM-dd HH:mm:ss");
					modelRecord.set(strFieldName, strValue);
				} catch (Exception e) {
				}

				break;
			}
			case Types.BINARY:
			case Types.LONGVARBINARY: {
				byte[] bysValue = null;
				InputStream stream = null;
				try {
					stream = rs.getBinaryStream(i + 1);
					bysValue = new byte[stream.available()];
					stream.read(bysValue);
				} finally {
					IOUtils.closeStream(stream);
				}

				modelRecord.set(strFieldName, bysValue);
				break;
			}
			case Types.BLOB: {
				byte[] bysValue = null;
				Blob blobValue = rs.getBlob(i + 1);
				bysValue = blobValue.getBytes(1, (int) blobValue.length());
				modelRecord.set(strFieldName, bysValue);
				break;
			}
			default:
				throw new SQLException("Unsupported sql data type " + nFieldType + " on field " + strFieldName);
			}
		}
		return 1;
	}
}

